#include "HttpUtil.h"
#include "../config/ConfigManager.h"
#include <QNetworkRequest>
#include <QByteArray>

using json = nlohmann::json;

#define CONFIG_PATH ":/config/config.json"

void HttpUtil::httpInit() {
    if (!ConfigManager::instance().isLoaded()) {
        ConfigManager::instance().loadConfig(CONFIG_PATH);
    }
    m_baseUrl = ConfigManager::instance().getString("http.baseUrl");
    m_initialized = true;
}

HttpUtil::HttpUtil(QObject *parent)
    : QObject(parent), m_manager(new QNetworkAccessManager(this)) {
}

HttpUtil::~HttpUtil() {
    m_manager->deleteLater();
}

HttpUtil &HttpUtil::instance() {
    static HttpUtil instance;
    return instance;
}

void HttpUtil::get(const QString &url,
                   const std::function<void(const json &)> &successCallback,
                   const std::function<void(const QString &)> &errorCallback,
                   const QMap<QString, QString> &headers) {
    sendRequest(url, "GET", {}, successCallback, errorCallback, headers);
}

void HttpUtil::post(const QString &url,
                    const json &data,
                    const std::function<void(const json &)> &successCallback,
                    const std::function<void(const QString &)> &errorCallback,
                    const QMap<QString, QString> &headers) {
    sendRequest(url, "POST", data, successCallback, errorCallback, headers);
}

void HttpUtil::put(const QString &url,
                   const json &data,
                   const std::function<void(const json &)> &successCallback,
                   const std::function<void(const QString &)> &errorCallback,
                   const QMap<QString, QString> &headers) {
    sendRequest(url, "PUT", data, successCallback, errorCallback, headers);
}

void HttpUtil::del(const QString &url,
                   const std::function<void(const json &)> &successCallback,
                   const std::function<void(const QString &)> &errorCallback,
                   const QMap<QString, QString> &headers) {
    sendRequest(url, "DELETE", {}, successCallback, errorCallback, headers);
}

void HttpUtil::sendRequest(const QString &url,
                           const QString &method,
                           const json &data,
                           const std::function<void(const json &)> &successCallback,
                           const std::function<void(const QString &)> &errorCallback,
                           const QMap<QString, QString> &headers) {
    if (!m_initialized) {
        httpInit();
    }

    QNetworkRequest request;
    QString fullUrl = m_baseUrl + url;
    request.setUrl(QUrl(fullUrl));
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");

    for (auto it = headers.constBegin(); it != headers.constEnd(); ++it) {
        request.setRawHeader(it.key().toUtf8(), it.value().toUtf8());
    }

    QNetworkReply *reply = nullptr;

    if (method == "GET") {
        reply = m_manager->get(request);
    } else if (method == "POST") {
        reply = m_manager->post(request, QByteArray::fromStdString(data.dump()));
    } else if (method == "PUT") {
        reply = m_manager->put(request, QByteArray::fromStdString(data.dump()));
    } else if (method == "DELETE") {
        reply = m_manager->deleteResource(request);
    }

    if (reply) {
        connect(reply, &QNetworkReply::finished, this, [this, reply, successCallback, errorCallback]() {
            handleResponse(reply, successCallback, errorCallback);
            reply->deleteLater();
        });
    } else {
        if (errorCallback) {
            errorCallback("Failed to create network request");
        }
    }
}

void HttpUtil::handleResponse(QNetworkReply *reply,
                              const std::function<void(const json &)> &successCallback,
                              const std::function<void(const QString &)> &errorCallback) {
    if (reply->error() != QNetworkReply::NoError) {
        if (errorCallback) {
            errorCallback(reply->errorString());
        }
        return;
    }

    QByteArray responseData = reply->readAll();
    try {
        json jsonResponse = json::parse(responseData.constData());
        if (successCallback) {
            successCallback(jsonResponse);
        }
    } catch (const std::exception &e) {
        if (errorCallback) {
            errorCallback(QString("JSON parse error: %1").arg(e.what()));
        }
    }
}
